package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/fs"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"
)

// 全局变量保存日志文件句柄
var globalLogFile *os.File

func main() {
	// 初始化日志
	initLog()
	defer func() {
		if globalLogFile != nil {
			err := globalLogFile.Close()
			if err != nil {
				log.Fatalf("关闭日志文件出错：%v", err)
				return
			}
		}
	}()

	var path string
	if len(os.Args) > 1 {
		filePath := os.Args[1]
		path = filePath
		log.Printf("传参路径：%v", path)
	} else {
		filePath, _ := os.Getwd()
		path = filePath
		log.Printf("当前路径：%v", path)
	}

	allFile, _ := getAllFileIncludeSubFolder(path)
	config := loadConfig()

	// 直接解密类型
	var decodeTypes = config.DecodeTypes
	// 映射解密类型
	var mappingTypes = config.MappingTypes

	log.Printf("待解密文件数量：%v", len(allFile))
	for _, path := range allFile {

		// 直接解密类型
		var ext = filepath.Ext(path)
		if ext == "" {
			log.Printf("跳过没有后缀文件：%v", path)
			continue
		}
		if strings.Contains(strings.Join(decodeTypes, ","), ext) {
			dstFilePath := path + ".temp"
			copyFile(path, dstFilePath)

			errRemove := os.Remove(path)
			if errRemove != nil {
				log.Printf("删除原始文件出错，path：%v，errRemove：%v", path, errRemove)
			}

			renameFile(dstFilePath, path)
			continue
		}

		// 映射解密类型
		descExt, exists := mappingTypes[ext]
		if exists {
			dstFilePath := path + descExt
			renameFile(path, dstFilePath)

			newDstFilePath := path + ".temp"
			copyFile(dstFilePath, newDstFilePath)

			err := os.Remove(dstFilePath)
			if err != nil {
				log.Printf("映射解密失败：%v", path)
			}

			renameFile(newDstFilePath, path)
		}
	}

	var now = time.Now()
	formattedTime := now.Format("2006-01-02 15:04:05")
	log.Printf("解密完成：%v", formattedTime)
}

func initLog() {
	// 打开指定的文件用于写入日志
	var logPath, _ = loadFilePath("logfile.log")
	globalLogFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0666)
	if err != nil {
		log.Fatalf("打开日志文件出错：%v", err)
	}

	// 将日志输出设置为我们刚刚打开的文件
	log.SetOutput(globalLogFile)
}

func renameFile(sourcePath, dstFilePath string) {
	unlockPath, _ := loadFilePath("Unlock.exe")
	arg := fmt.Sprintf(` -sourcePath="%v" -destPath="%v"`, sourcePath, dstFilePath)
	cmd := exec.Command(unlockPath)
	cmd.SysProcAttr = &syscall.SysProcAttr{CmdLine: "/c" + arg}
	output, err := cmd.Output()
	if err != nil {
		log.Fatalf("执行命令出错：%v", err)
	}

	info := string(output)
	if info != "" {
		log.Println(info)
	}
}

func copyFile(sourcePath, targetPath string) {
	sourceFile, err := os.Open(sourcePath)
	if err != nil {
		log.Fatalf("打开源文件出错：%v", err)
	}
	defer func(sourceFile *os.File) {
		err := sourceFile.Close()
		if err != nil {
			log.Fatalf("打开源文件出错：%v", err)
		}
	}(sourceFile)

	targetFile, err := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		log.Fatalf("创建目标文件出错：%v", err)
	}
	defer func(targetFile *os.File) {
		err := targetFile.Close()
		if err != nil {
			log.Fatalf("打开目标文件出错：%v", err)
		}
	}(targetFile)

	_, err = io.Copy(targetFile, sourceFile)
	if err != nil {
		log.Fatalf("复制文件出错：%v", err)
	}
}

// getAllFileIncludeSubFolder
//
//	@Description: 获取目录下所有文件（包含子目录）
//	@param folder
//	@return []string
//	@return error
func getAllFileIncludeSubFolder(folder string) ([]string, error) {
	var result []string
	err := filepath.Walk(folder, func(path string, info fs.FileInfo, err error) error {
		if err != nil {
			log.Println(err.Error())
			return err
		}
		if !info.IsDir() {
			result = append(result, path)
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return result, nil
}

type Config struct {
	DecodeTypes  []string          `json:"decodeTypes"`
	MappingTypes map[string]string `json:"mappingTypes"`
}

func loadConfig() (res Config) {
	content, err := loadFileFromUserHome("config.ini")
	if err != nil {
		log.Fatalf("读取json配置文件出错：%v", err)
	}

	var config Config
	err = json.Unmarshal(content, &config)
	if err != nil {
		log.Fatalf("解析json配置文件出错：%v", err)
	}

	return config
}

func loadFileFromUserHome(fileName string) ([]byte, error) {
	var configPath, err = loadFilePath(fileName)
	if err != nil {
		log.Printf("获取文件路径出错：%v", fileName)
	}
	return os.ReadFile(configPath)
}

func loadFilePath(fileName string) (string, error) {
	homeDir, err := os.UserHomeDir()
	if err != nil {
		return "", err
	}
	configPath := filepath.Join(homeDir, ".unlock", fileName)
	return configPath, nil
}
